﻿using System;

namespace Mitosoft.Definitions.Range
{
  /// <summary>
  /// Represents a range.
  /// </summary>
  /// <typeparam name="T">Some type being able to be compared.</typeparam>
  public class Range<T> where T : IComparable<T>
  {
    #region fields

    /// <summary>
    /// Represents <see cref="Start"/>
    /// </summary>
    protected T _start;

    /// <summary>
    /// Represents <see cref="End"/>
    /// </summary>
    protected T _end;

    #endregion

    #region constructors

    /// <summary>
    /// Creates a new <see cref="Range"/> instance.
    /// </summary>
    /// <param name="start">Start of the range.</param>
    /// <param name="end">End of the range.</param>
    public Range(T start, T end)
    {
      _start = start;
      _end = end;

      CheckAndCorrectStartAndEnd();
    }

    #endregion

    #region properties

    /// <summary>
    /// Starting point of the range.
    /// </summary>
    public T Start
    {
      get { return _start; }
      protected set { _start = value; }
    }

    /// <summary>
    /// End point of the range.
    /// </summary>
    public T End
    {
      get { return _end; }
      protected set { _end = value; }
    }

    #endregion

    #region methods

    /// <summary>
    /// Checks <see cref="Start"/> and <see cref="End"/>
    /// and exchange both values if End is not bigger as Start.
    /// </summary>
    protected void CheckAndCorrectStartAndEnd()
    {
      if (Start.CompareTo(End) <= 0) return;

      // Exchange start and end:
      T temp = Start;
      Start = End;
      End = temp;
    }

    /// <summary>
    /// Gets the overlapping range.
    /// </summary>
    /// <param name="range1">First range to be checked.</param>
    /// <param name="range2">Second range to be checked.</param>
    /// <returns>The overlapping range result.</returns>
    public static Range<T> OverlappingRange(Range<T> range1, Range<T> range2)
    {
      if (range1 == null || range2 == null) 
        return null;  // No overlapping possible

      T resultingStart = range1.Start.CompareTo(range2.Start) > 0 ? range1.Start : range2.Start;
      T resultingEnd = range1.End.CompareTo(range2.End) < 0 ? range1.End : range2.End;

      return resultingStart.CompareTo(resultingEnd) > 0 ? null : new Range<T>(resultingStart, resultingEnd);
    }

    /// <summary>
    /// Get the overlapping range.
    /// </summary>
    /// <param name="range">Range to be checked.</param>
    /// <returns>The overlapping range result.</returns>
    public Range<T> OverlappingRange(Range<T> range)
    {
      if (range == null) 
        return null;  // No overlapping possible

      T resultingStart = Start.CompareTo(range.Start) > 0 ? Start : range.Start;
      T resultingEnd = End.CompareTo(range.End) < 0 ? End : range.End;

      return resultingStart.CompareTo(resultingEnd) > 0 ? null : new Range<T>(resultingStart, resultingEnd);
    }

    #endregion

  }
}
